﻿using System;
using System.Threading;
using System.Windows.Threading;
using MugenMvvmToolkit.Interfaces;
#if TASKSUPPORT
using System.Threading.Tasks;
#endif

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    /// Represents the default implemenation of <see cref="IThreadManager"/>.
    /// </summary>
    public class ThreadManager : IThreadManager
    {
        #region Fields

        private Dispatcher _uiDispatcher;

        #endregion

        #region Constructor

#if TASKSUPPORT
        /// <summary>
        ///     Initializes a new instance of the <see cref="ThreadManager" /> class.
        /// </summary>
        public ThreadManager(Dispatcher uiDispatcher, TaskScheduler uiTaskSheduler, TaskScheduler nonUiTaskSheduler)
        {
            Should.NotBeNull(uiDispatcher, "uiDispatcher");
            Should.NotBeNull(uiTaskSheduler, "uiTaskSheduler");
            Should.NotBeNull(nonUiTaskSheduler, "nonUiTaskSheduler");
            UiDispatcher = uiDispatcher;
            UiTaskScheduler = uiTaskSheduler;
            NonUiTaskScheduler = nonUiTaskSheduler;
#if WPF
            AsynchronousPriority = DispatcherPriority.DataBind;
            SynchronousPriority = DispatcherPriority.Send;
#endif
        }
#else
        /// <summary>
        ///     Initializes a new instance of the <see cref="ThreadManager" /> class.
        /// </summary>
        public ThreadManager(Dispatcher uiDispatcher)
        {
            Should.NotBeNull(uiDispatcher, "uiDispatcher");
            UiDispatcher = uiDispatcher;
#if WPF
            AsynchronousPriority = DispatcherPriority.DataBind;
            SynchronousPriority = DispatcherPriority.Send;
#endif
        }
#endif
        #endregion

        #region Implementation of IThreadManager

        /// <summary>
        ///     Gets or sets a dispatcher associated with current UI thread.
        /// </summary>
        public Dispatcher UiDispatcher
        {
            get
            {
                Should.PropertyBeNotNull(_uiDispatcher, "UiDispatcher");
                return _uiDispatcher;
            }
            set
            {
                Should.PropertyBeNotNull(value, "UiDispatcher");
                _uiDispatcher = value;
            }
        }

        /// <summary>
        /// Determines whether the calling thread is the UI thread.
        /// </summary>        
        /// <returns><c>true</c> if the calling thread is the UI thread; otherwise, <c>false</c>.</returns>
        public bool IsUiThread
        {
            get { return UiDispatcher.CheckAccess(); }
        }

#if WPF
        /// <summary>
        /// Gets or sets the priority of asynchronous operations.
        /// </summary>
        public DispatcherPriority AsynchronousPriority { get; set; }

        /// <summary>
        /// Gets or sets the priority of synchronous operations.
        /// </summary>
        public DispatcherPriority SynchronousPriority { get; set; }

        /// <summary>
        ///     Invokes an action on the UI thread using the UiDispatcher.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        /// <param name="priority">The specified <see cref="DispatcherPriority"/> to invoke the action.</param>
        public virtual void InvokeInUi(Action action, DispatcherPriority priority)
        {
            if (IsUiThread)
            {
                action();
                return;
            }
            UiDispatcher.BeginInvoke(priority, action);
        }

        /// <summary>
        ///     Invokes an action on the UI thread synchronous using the UiDispatcher.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        /// <param name="priority">The specified <see cref="DispatcherPriority"/> to invoke the action.</param>
        public virtual void InvokeInUiSynchronous(Action action, DispatcherPriority priority)
        {
            if (IsUiThread)
            {
                action();
                return;
            }

            UiDispatcher.Invoke(priority, action);
        }
#endif

        /// <summary>
        ///     Invokes an action on the UI thread.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        public void InvokeInUi(Action action)
        {
#if WPF
            InvokeInUi(action, AsynchronousPriority);
#else
            if (IsUiThread)
            {
                action();
                return;
            }
            UiDispatcher.BeginInvoke(action);
#endif
        }

        /// <summary>
        ///     Invokes an action on the UI thread synchronous.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        public void InvokeInUiSynchronous(Action action)
        {
#if WPF
            InvokeInUiSynchronous(action, SynchronousPriority);
#else
            if (IsUiThread)
            {
                action();
                return;
            }
            using (EventWaitHandle wait = new AutoResetEvent(false))
            {
                UiDispatcher.BeginInvoke(() =>
                                             {
                                                 action();
                                                 wait.Set();
                                             });
                wait.WaitOne();
            }
#endif
        }

        /// <summary>
        ///     Invokes an action asynchronous.
        /// </summary>
        /// <param name="action">
        ///     The specified <see cref="Action" />.
        /// </param>
        /// <param name="state">The specified state.</param>
        public virtual void InvokeAsync(Action<object> action, object state)
        {
            ThreadPool.QueueUserWorkItem(o => action(o), state);
        }


#if TASKSUPPORT
        /// <summary>
        /// Gets or sets a <see cref="TaskScheduler"/> associated with current UI thread.
        /// </summary>
        public TaskScheduler UiTaskScheduler { get; set; }

        /// <summary>
        /// Gets or sets a <see cref="TaskScheduler"/> not associated with current UI thread.
        /// </summary>
        public TaskScheduler NonUiTaskScheduler { get; set; }

#endif
        #endregion
    }
}